package cn.wolfcode.p2p.base.service.impl;

import cn.wolfcode.p2p.base.domain.Account;
import cn.wolfcode.p2p.base.domain.IpLog;
import cn.wolfcode.p2p.base.domain.LoginInfo;
import cn.wolfcode.p2p.base.domain.UserInfo;
import cn.wolfcode.p2p.base.exception.DisplayableException;
import cn.wolfcode.p2p.base.mapper.LoginInfoMapper;
import cn.wolfcode.p2p.base.service.*;
import cn.wolfcode.p2p.base.util.AsserUtil;
import cn.wolfcode.p2p.base.util.Constants;
import cn.wolfcode.p2p.base.util.MD5;
import cn.wolfcode.p2p.base.util.UserContext;
import cn.wolfcode.p2p.bussness.domain.ExpAccount;
import cn.wolfcode.p2p.bussness.service.IExpAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
@Transactional//事务注解管理
public class LoginInfoServiceImpl implements ILoginInfoService {

    @Autowired
    private LoginInfoMapper loginInfoMapper;

    @Autowired
    private ISendVerifyCodeService verifyCodeService;

    @Autowired
    private IAccountService accountService;

    @Autowired
    private IUserInfoService userInfoService;

    @Autowired
    private IIpLogService iIpLogService;

    @Autowired
    private IExpAccountService expAccountService;

    @Override
    public void userRegister(String username, String verifyCode,
                             String password, String confirmPwd) {
        //判断传进来的字段非空
        AsserUtil.isNotNull(username, "手机号码不能为空");
        AsserUtil.isNotNull(verifyCode, "短信验证码不能为空");
        AsserUtil.isNotNull(password, "密码不能为空");
        AsserUtil.isNotNull(confirmPwd, "确认密码不能为空");

        //判断密码和确认密码需要一致
        AsserUtil.isEqual(password, confirmPwd, "密码与确认密码不一致");

        //判断短信验证码 TODO 传入手机号码 和 验证码 判断
        boolean isVaild = verifyCodeService.validate(username, verifyCode);
        //如果返回false 则应该提示信息给用户
        if (!isVaild) {
            throw new DisplayableException("验证码或者手机号码错误,请重新输入!");
        }

        //判断账户名是否已经存在数据库中
        int count = loginInfoMapper.checkUsername(username);
        //如果返回的count大于0的话 则该用户名已经存在数据库中
        if (count > 0) {
            throw new DisplayableException("该用户名已经注册了!");
        }

        //创建LoginInfo对象 插入到数据库
        LoginInfo info = new LoginInfo();
        info.setUsername(username);
        //密码需要加密 保存到对象中
        info.setPassword(MD5.encoder(password));
        //设置为前台用户
        info.setUserType(LoginInfo.USERTYPE_NOMAL);
        //把对象保存到数据库中
        loginInfoMapper.insert(info);

        //==============创建账号的同时, 创建account对象 和 userInfo对象==================
        Account account = new Account();
        //把账号的id设进到account对象和 userInfo对象的ID里面
        account.setId(info.getId());
        //前台的设置密码 设置到交易密码到account对象中
        account.setTradePassword(info.getPassword());
        //插入到数据库中
        accountService.save(account);

        UserInfo userInfo = new UserInfo();
        //设置ID
        userInfo.setId(info.getId());
        //设置手机号码
        userInfo.setPhoneNumber(info.getUsername());
        userInfoService.save(userInfo);

        //=========创建体验金账户============
        ExpAccount expAccount = new ExpAccount();
        //把账号的id设进到expAccount对象
        expAccount.setId(info.getId());
        //保存到数据库
        expAccountService.save(expAccount);

        this.expAccountService.grantExpMoney(expAccount.getId(),new IExpAccountService.LastTime(1, IExpAccountService.LastTimeUnit.MONTH),
                Constants.INIT_EXPACCOUNT_MONEY,Constants.EXPMONEY_TYPE_REGISTER);
    }

    @Override
    public boolean validateUsername(String username) {
        return loginInfoMapper.checkUsername(username) <= 0;
    }

    @Override
    public LoginInfo login(String username, String password, int userType) {
        LoginInfo loginInfo = null;

        //=============当前用户名检查===================
        LoginInfo info = loginInfoMapper.getUsername(username, userType);
        if (info == null) {
            throw new DisplayableException("账号和密码不匹配,请重新输入");
        }

        //拿到当前用户的id
        Long userId = info.getId();

        //获取其输错密码的次数
        int count = info.getMissNumber();

        //获取其最新输错密码的时间
        Date dateMissTime = info.getMissTime();

        //获取其允许登陆时间
        Date dateAllowTime = info.getAllowTime();

        //当前登陆账户的时间
        Date dateNow = new Date();

        //===登录的成功 或者 失败 都记录在日志中===
        IpLog ipLog = new IpLog();

        //把传进的用户名设置进去ipLog对象中 而不是登陆成功或者失败才设置进去
        ipLog.setUsername(username);

        //设置ip的用户类型
        ipLog.setUserType(userType);

        //把当前登陆时间设置进去对象的时间
        ipLog.setLoginTime(new Date());

        //设置当前的ip
        ipLog.setIp(UserContext.getIp());

        //判断当前用户是前端用户才执行下面的方法 不是的话
        //如果当前允许登陆时间为null 或者当前时间超过解封账户的时间
        if (dateAllowTime == null || dateNow.getTime() >= dateAllowTime.getTime()) {
            //判断loginInfo是否为null
            loginInfo = loginInfoMapper.login(username, password, userType);
            //判断输入密码错误超过三次了
            if (count >= 3) {
                //允许登陆时间 测试加三分钟 真实的是加一个小时
                Date dateAfterAllowTime = new Date(dateAllowTime.getTime() + 180000);
                //创建登陆用户对象
                LoginInfo userInfo = new LoginInfo();
                //修改数据库中的错误次数清零
                userInfo.setMissNumber(0);
                //修改最后登陆时间
                userInfo.setMissTime(new Date());
                //修改允许登陆的最新时间
                userInfo.setAllowTime(dateAfterAllowTime);
                //设置用户的ID
                userInfo.setId(userId);
                //更新登陆用户的信息
                loginInfoMapper.updateByUserInfo(userInfo);
                //登陆失败 设置状态为失败
                ipLog.setState(IpLog.STATE_FAILED);
                //return userInfo;
                //throw new RuntimeException("当前用户名密码输错三次,冻结账号一分钟");
            } else {
                //错误次数小于三次 并且用户名和密码都正确了
                if (loginInfo != null) {
                    //把输错密码次数清零
                    count = 0;
                    //记录最新登陆时间
                    dateMissTime = new Date();
                    //把当前时间设置为最新允许登陆时间
                    dateAllowTime = new Date();
                    //创建登陆用户对象
                    LoginInfo userInfo = new LoginInfo();
                    //设置用户ID
                    userInfo.setId(userId);
                    //设置最新登陆时间
                    userInfo.setMissTime(dateMissTime);
                    //设置最新允许登陆时间
                    userInfo.setAllowTime(dateAllowTime);
                    //设置清零后的密码错误次数
                    userInfo.setMissNumber(count);
                    //更新到数据库中
                    loginInfoMapper.updateByUserInfo(userInfo);
                    //把登陆信息存入session中
                    UserContext.setCurrent(loginInfo);
                    //把登陆用户的用户信息存入session中
                    UserContext.setUserInfoCurrent(userInfoService.get(loginInfo.getId()));
                    //登陆成功的话 把设置日志的状态为成功
                    ipLog.setState(IpLog.STATE_SUCCESS);
                    //插入到数据库中
                    iIpLogService.save(ipLog);
                    return userInfo;
                } else {
                    //如果密码错误了 在count + 1
                    count = count + 1;
                    //当前输错密码的时间
                    dateMissTime = new Date();
                    //当前允许登陆时间
                    dateAllowTime = new Date();
                    //创建登陆用户对象
                    LoginInfo userInfo = new LoginInfo();
                    //设置用户ID
                    userInfo.setId(userId);
                    //设置错误次数
                    userInfo.setMissNumber(count);
                    //设置最新登陆时间
                    userInfo.setMissTime(dateMissTime);
                    //设置允许时间为当前登陆时间
                    userInfo.setAllowTime(dateAllowTime);
                    //更新到数据库
                    loginInfoMapper.updateByUserInfo(userInfo);
                    //登陆失败 设置状态为失败
                    ipLog.setState(IpLog.STATE_FAILED);
                    //如果登陆失败的 当前会抛出异常的话 并且回滚事务 失败的记录也就保存不到数据库中了
                    // 不抛异常了 直接在controller里面返回result的信息就行了
                    //throw new DisplayableException("账号或者用户名有误,请重新输入");
                    //插入到数据库中
                    iIpLogService.save(ipLog);
                    return userInfo;
                }
            }
        }
        //插入到数据库中
        iIpLogService.save(ipLog);
        return loginInfo;
    }

    @Override
    public void initAdmin() {
        //判断数据库有没有管理员
        int count = loginInfoMapper.selectCountByUserType(LoginInfo.USERTYPE_ADMIN);
        //如果没有 那就创建一个出来
        if (count == 0) {
            LoginInfo loginInfo = new LoginInfo();
            //设置用户类型
            loginInfo.setUserType(LoginInfo.USERTYPE_ADMIN);
            //设置用户名
            loginInfo.setUsername(Constants.DEFAULT_ADMIN_ACCOUNT);
            //设置密码 记得加密
            loginInfo.setPassword(MD5.encoder(Constants.DEFAULT_ADMIN_PASSWORD));
            //插入到数据库中
            loginInfoMapper.insert(loginInfo);
        }
    }

    @Override
    public List<LoginInfo> selectIsAuditorList() {
        return loginInfoMapper.selectIsAuditorList();
    }

    @Override
    public LoginInfo get(Long id) {
        return loginInfoMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<Map<String, Object>> videoAuthAuto(String username, int userType) {
        return loginInfoMapper.videoAuthAuto(username,userType);
    }
}
